﻿package fwk.loaderManager {	

	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.IEventDispatcher;
	import flash.events.EventDispatcher;
	import flash.net.FileReference;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.display.Loader;
   	import flash.system.ApplicationDomain;
   	import flash.system.SecurityDomain;
   	import flash.system.LoaderContext;
	import flash.text.StyleSheet;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.system.Capabilities;
	

	
	import fwk.loaderManager.events.*;
	
	
	 /**
	 * Dispatched during the loading
	 * @eventType fwk.loaderManager.events.LoaderEvent
	 */
	 [Event(name = "PROGRESS", type = "fwk.loaderManager.events.LoaderEvent")]
	 
	 /**
	 * Dispatched when the loading of the stake is complete
	 * @eventType fwk.loaderManager.events.LoaderEvent
	 */
	 [Event(name = "COMPLETED", type = "fwk.loaderManager.events.LoaderEvent")]
	
	/**
	 * To load a list of ressources
	 * 
	 * @usage <code>		var loaderManager:LoaderManager	= new LoaderManager();
	 *  <br />loaderManager.idLastCache = "20-01-2010";
	 *	<br />loaderManager.noCache = true;
	 *	<br />loaderManager.addEventListener(LoaderEvent.PROGRESS, onProgressAll);
	 *	<br />loaderManager.addEventListener(LoaderEvent.COMPLETED, onCompletedAll);
	 *	<br />	
	 *	<br />	
	 *	<br />function onProgressAll(evt:LoaderEvent):void
	 *	<br />{
	 *	<br />	
	 *	<br />	
	 *	<br />}
	 *	<br />function onCompletedAll(evt:LoaderEvent):void
	 *  <br />{
	 *	<br />
	 *	<br />}
	 *  <br /></code>
	*/


	 
	 
	public class LoaderManager extends EventDispatcher
	{
		
		private var _tabObjectInit:Array;
		private var _tabObjectToLoad:Array;
		private var _currentItem:LoaderItemObject;
		private var _currentItemGroup:LoaderItemGroup;
		
		// pour traiter les groupes d'items
		private var _isGroupItem:Boolean;
		private var _tabObjectGroup:Array;
		
		private var _isPriority:Boolean;
		
		private var _nbItem:int;
		private var _nbItemGroup:int;
		private var _nbItemLoaded:int;
		
		private var _percent:Number;
		private var _percentCurrent:Number;
		private var _currentLabel:String;
		
		private	var _loader:Loader;
		private	var _urlLoader:URLLoader;
		
		//gestion cache
		private var _noCache:Boolean = false;
		private var _idLastCache:String = "default";
		
		// total list size
		private var _bytesTotal:Number = 0;
		
		private var _isPause:Boolean = false;
		
	

		
		
		/**
		 * Create a instance of LoaderManager
		 * @param tabObjectToLoad  An array of LoaderItemObject or LoaderItemGroug to load. This array represent the stake to load. Default null.
		 */
		function LoaderManager(tabObjectToLoad:Array = null):void
		{
			/*trace("//////////////////////////////////////////////");
			trace("/// new LoaderManager");
			trace("//////////////////////////////////////////////");*/

			reset();
			if (tabObjectToLoad != null)
			{
				_tabObjectToLoad = tabObjectToLoad;
				_tabObjectInit = _tabObjectToLoad..concat();
				for (var i:int = 0; i < _tabObjectToLoad.length; i++)
				{
					if (_tabObjectToLoad[i] is LoaderItemGroup)
					{
						var itemGoup:LoaderItemGroup = _tabObjectToLoad[i] as LoaderItemGroup;
						for (var j:String  in itemGoup.tabItem)
						{
							_nbItem ++;
						}
					}else {
						_nbItem++;
					}
				}
			}
		}
		
		/**
		 * The array of LoaderItemObject or LoaderItemGroug to load. This array represent the stake to load.
		* @param  val The array of LoaderItemObject or LoaderItemGroug to load. This array represent the stake to load.
		 */
		public function set tabToLoad(val:Array):void
		{
			_tabObjectToLoad = val;
			for (var i:int = 0; i < _tabObjectToLoad.length; i++)
			{
				if (_tabObjectToLoad[i] is LoaderItemGroup)
				{
					var itemGoup:LoaderItemGroup = _tabObjectToLoad[i] as LoaderItemGroup;
					for (var j:String  in itemGoup.tabItem)
					{
						_nbItem ++;
					}
				}else {
					_nbItem++;
				}
			}
		}
		/**
		* Get the LoaderItemObject or LaoderItemGroup with the id parameter
		* @param id An id of LoaderItemObject or LoaderItemGroup exist in the current stake
		* @return A LoaderItemObject or a LoaderItemGroup if exist else return null
		 */
		public function getLoaderItem(id:String):Object
		{
			for (var i:int = 0; i < _tabObjectInit.length; i++) {
				
				/*if (_tabObjectInit[i] is LoaderItemGroup) {
					var lg:LoaderItemGroup = _tabObjectInit[i] as LoaderItemGroup;
					for (var p:String in lg.tabItem)
					{
						if (lg.tabItem[p].id == id) return lg.tabItem[p];
					}	
				}
				else {
					if (_tabObjectInit[i].id == id) return _tabObjectInit[i];
				}*/
				if (_tabObjectInit[i].id == id) return _tabObjectInit[i];
				
			}
			
			return null;
		}
		/**
		* Enable or disable the cache of the internet browser. Default false
		* @param val true or false
		 */
		public function set noCache(val:Boolean):void
		{
			_noCache = val;
		}
		/**
		* Enable or disable the cache of the internet browser. Default false
		* @return true or false
		 */
		public function get noCache():Boolean {
			return _noCache;
		}
		/**
		* A id of the cache of the internet browser to use
		* @param val A id string of the cache of the internet browser to use. Default "default"
		 */
		public function set idLastCache(val:String):void {
			_idLastCache = val;
		}	
		/**
		* A id of the cache of the internet browser to use
		* @param val A id string of the cache of the internet browser to use. Default "default"
		 */
		public function get idLastCache():String {
			return _idLastCache;
		}
		/**
		* The percent loaded of the stake
		* @return A value between 0 and 1
		 */
		public function get percent():Number {
			return _percent;
		}
		/**
		* The percent loaded of the current LoaderItemObject or LoaderItemGroup is loading
		* @return A value between 0 and 1
		 */
		public function get percentCurrent():Number {
			return _percentCurrent;
		}
		/**
		* The label of the current LoaderItemObject or LoaderItemGroup is loading. 
		* @return A string. The value is "" if no label.
		 */
		public function get currentLabel():String {
			return _currentLabel;
		}
		/**
		 * Reset the loaderManager. Clear the current stake of LoaderItemObject and LoaderItemGroup
		 */
		public function reset():void
		{

			
			try
			{
				removeFileListeners();
				_urlLoader.close();
			}catch (e:Error)
			{
				
			}
			try
			{
				if (_loader != null)
				{

					removeMovieListeners();
					
					
					_loader.close();
					_loader.contentLoaderInfo.addEventListener(Event.UNLOAD, onUnloadMovie);
					_loader.unload();
					
					function onUnloadMovie(e:Event):void
					{
						_loader.contentLoaderInfo.removeEventListener(Event.UNLOAD, onUnloadMovie);
					}
				}
			}catch(e:Error)
			{
				
			}
			
			
			_tabObjectToLoad 	= new Array();
			_tabObjectInit 		= new Array();
			_tabObjectGroup		= new Array();
			
			_isPriority 		= false;
			_isGroupItem		= false;
			_currentItem		= null;
			_currentItemGroup	= null;
			_currentLabel 		= "";
			_percent 			= 0;
			_percentCurrent 	= 0;
			_nbItem 			= 0;
			_nbItemLoaded 		= 0;
			_nbItemGroup		= 0;
			_loader				= null;
			_urlLoader 			= null;
			_bytesTotal 		= 0;

		}
		/**
		 * Stop the current loading.
		 */
		public function pause():void
		{
			//trace("pause loaderManager");
			
			_isPause = true;
			
			// on arrete le chargement en cours
			try
			{
				removeFileListeners();
				_urlLoader.close();
			}catch (e:Error)
			{
				
			}
			try
			{
				if (_loader != null)
				{

					removeMovieListeners();
					
					
					_loader.close();
					_loader.contentLoaderInfo.addEventListener(Event.UNLOAD, onUnloadMovie);
					_loader.unload();
					
					function onUnloadMovie(e:Event):void
					{
						_loader.contentLoaderInfo.removeEventListener(Event.UNLOAD, onUnloadMovie);
					}
				}
			}catch(e:Error)
			{
				
			}
			
			
			
		}
		

		/**
		 * Resume the current loading
		 */
		public function resume():void
		{
			
			
			_isPause = false;
			

			/*trace("");
			trace("-----------------------------------------------");
			trace("RESUME LOAD LIST,  NB ITEMS : " + _tabObjectToLoad.length);
			trace("");*/
			loadNext();
		}
			
			
		
		
		/**
		 * Add a LoaderItemObject or LoaderItemGroup to the stake
		 * @param objToLoad A LoaderItemObject or LoaderItemGroup
		 */
		public function add(objToLoad:Object):void
		{	
			_tabObjectToLoad.push(objToLoad);
			_tabObjectInit.push(objToLoad);
			if (objToLoad is LoaderItemGroup)
			{
				var itemGoup:LoaderItemGroup = objToLoad as LoaderItemGroup;
				var k:int = 0;
				for (var j:String  in itemGoup.tabItem)
				{
					_nbItem ++;
					k++;
				}
				if( k == 0 ) {
					_nbItem++;
				}
			}else {
				_nbItem++;
			}
		}
		/**
		 * Start the loading of the stake
		 */
		public function start():void
		{
			if (_tabObjectToLoad.length > 0 && !_isPause)
			{
				trace("");
				trace("-----------------------------------------------");
				trace("START LOAD LIST,  NB ITEMS : " + _tabObjectToLoad.length);
				trace("");
				loadNext();
			}else {
					// déclenchement event
					var evt:LoaderEvent = new LoaderEvent(LoaderEvent.COMPLETED);
					evt.percent = 1;
					this.dispatchEvent(evt);
			}
		}
		/**
		 * To load in priority a LoaderItemGroup or a LoaderItemObject during the current stake is loading
		 * @param ...args A list of LoaderItemObject or LoaderItemGroup
		 */
		public function loadInPriority(...args):void
		{
			
			
			try
			{
				removeFileListeners();
				_urlLoader.close();
			}catch (e:Error)
			{
				
			}
			try
			{
				if (_loader != null)
				{

					removeMovieListeners();
					
					
					_loader.close();
					_loader.contentLoaderInfo.addEventListener(Event.UNLOAD, onUnloadMovie);
					_loader.unload();
					
					function onUnloadMovie(e:Event):void
					{
						_loader.contentLoaderInfo.removeEventListener(Event.UNLOAD, onUnloadMovie);
						_loader = null;
						startPriority();
					}
				}else {
					startPriority();
				}
			}catch(e:Error)
			{
				startPriority();
			}
			
			
			function startPriority():void {
				_isPriority = true;
				_isGroupItem = false;
				_tabObjectGroup = new Array();
				
				
				_isGroupItem = false;
				for (var i:int = 0; i < args.length; i++)
				{
					addItemInPriority(args[i]);
				}
				
				loadNext();
			}

			

		}
		
		//-----------------------------------------------------------------------
		private function loadNext():void
		{
			
			
			if (_isGroupItem)
			{
				loadItem();

			}else
			{
				
				if (_tabObjectToLoad.length > 0)
				{
					if (_tabObjectToLoad[0] is LoaderItemObject && !(_tabObjectToLoad[0] as LoaderItemObject).completed)
					{
						loadItem();
					}else if (_tabObjectToLoad[0] is LoaderItemGroup && !(_tabObjectToLoad[0] as LoaderItemGroup).completed)
					{
						initItemsGroup();
						if (_tabObjectGroup.length > 0)
						{
							_isGroupItem = true;
							_currentItemGroup = _tabObjectToLoad[0];
							
							loadItem();
							
						}else {
							
							 _tabObjectToLoad[0].completed = true;
							_nbItemLoaded++;
							popItemFromTabItem();
							_isGroupItem = false;
							loadNext();
						}
					}else
					{
						_nbItemLoaded++;
						popItemFromTabItem();
						loadNext();
					}

				}else
				{
					
					trace("");
					trace("LOAD LIST COMPLETE,  TOTAL SIZE LOADED : " + Math.ceil(_bytesTotal / 1000) + " kb");
					trace("-----------------------------------------------");
					trace("");
					
					// déclenchement event
					var evt:LoaderEvent = new LoaderEvent(LoaderEvent.COMPLETED);
					evt.percent = _percent;
					this.dispatchEvent(evt);
				}


			}
			
		}

		
		//-----------------------------------------------------------------------
		private function initItemsGroup():void
		{
			_tabObjectGroup		= new Array();
			var itemGroup:LoaderItemGroup =  (_tabObjectToLoad[0] as LoaderItemGroup);
			trace("");
			trace("    ----------------------------------------------");
			trace("    Load Group id : " + itemGroup.id);
			for (var p:String in itemGroup.tabItem)
			{
				_tabObjectGroup.push(itemGroup.tabItem[p]);
			}
			
			_nbItemGroup = _tabObjectGroup.length;
		}
		//-----------------------------------------------------------------------
		private function loadItem():void
		{
				if (_isGroupItem)
					_currentItem =  (_tabObjectGroup[0] as LoaderItemObject);
				else 
					_currentItem =  (_tabObjectToLoad[0] as LoaderItemObject);
					
				var myType:String;
				// on traite le type de l'url à charger
				myType =  _currentItem.type;
				if(myType == null){
					var tabSplit:Array = _currentItem.url.split(".");
					myType = tabSplit[tabSplit.length-1];
				}
				
				_currentLabel = _currentItem.label;
				if (_currentItem.url != null)
				{
					trace("    ----------------------------------------------");
					trace("    Load : " + _currentItem.url);
					switch(myType){
						case "xml":
							loadFile();
							break;
						case "css":
							loadFile();
							break;
						case "swf":
							loadMovie();
							break;
						case "image":
							loadMovie();
							break;
						case "jpg":
							loadMovie();
							break;
						case "png":
							loadMovie();
							break;
						default:
							loadMovie();
							break;
					}
				}else {
					trace("XXX l'url à charger du LoaderItemObject n'est pas renseigné ");
				}
				


		}
		//-----------------------------------------------------------------------
		//------- loading File (xml, css, txt, ...)
		//-----------------------------------------------------------------------
		private function loadFile():void
		{
			_urlLoader = new URLLoader();
			_urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
			
			var urlToLoad:String = _currentItem.url;			
			if (Capabilities.playerType != "External") urlToLoad += "?" + _idLastCache;
			if (Capabilities.playerType != "External" && _noCache) urlToLoad  += "?" + (new Date()).getTime();
			var request:URLRequest = new URLRequest(urlToLoad);
			
			
			_urlLoader.addEventListener(Event.COMPLETE, onFileComplete);
			_urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onFileError);
			_urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onFileSecurityError);
			_urlLoader.addEventListener(ProgressEvent.PROGRESS, itemProgress);
			_urlLoader.load(request);
			
		}
		private function onFileSecurityError(eve:Event):void{
			trace(" XXXXX   onFileSecurityError");
			removeFileListeners();
			dispatchError();
		}
		private function onFileError(eve:Event):void{
			trace(" XXXXX   onFileError");
			removeFileListeners();
			dispatchError();
		}
		private function dispatchError():void {
			var evt:LoaderEvent = new LoaderEvent(LoaderEvent.LOAD_ERROR);
			_currentItem.dispatchEvent(evt);
		}
		private function onFileComplete(event:Event):void
		{
			removeFileListeners();	
			trace("        -------------------------------------");
			trace("        load complete, size : "  + Math.ceil(_currentItem.bytesTotal/1000) + " kb");
			trace("");
			
			_bytesTotal += _currentItem.bytesTotal;
			
			itemCompleted(_urlLoader.data);
				


		}
		private function removeFileListeners():void
		{
			
			_urlLoader.removeEventListener(Event.COMPLETE, onFileComplete);
			_urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, onFileError);
			_urlLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onFileSecurityError);
			_urlLoader.removeEventListener(ProgressEvent.PROGRESS, itemProgress);

		}
		
		//-----------------------------------------------------------------------
		//------- loading Movie (swf, images)
		//-----------------------------------------------------------------------
		private function loadMovie():void
		{


			_loader = new Loader();
			
			_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onMovieComplete);
			_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, itemProgress);
			_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onMovieError);

			
			// context
			var urlToLoad:String = _currentItem.url;			
			if (Capabilities.playerType != "External") urlToLoad += "?" + _idLastCache;
			if (Capabilities.playerType != "External" && _noCache) urlToLoad  += "?" + (new Date()).getTime();
			
			var context:LoaderContext = new LoaderContext();
			if (_currentItem.context) {
				context.applicationDomain = ApplicationDomain.currentDomain;
			}
			if (_currentItem.securityDomain && Capabilities.playerType != "External") {
				context.checkPolicyFile = true;
				context.securityDomain = SecurityDomain.currentDomain;
				
			}
			
			_loader.load(new URLRequest(urlToLoad), context);
			
			
		}
		

		private function onMovieComplete(event:Event):void
		{
			removeMovieListeners();
			
			
			trace("        -------------------------------------");
			trace("        load complete, size : "  + Math.ceil(_currentItem.bytesTotal/1000) + " kb");
			trace("");
			
			_bytesTotal += _currentItem.bytesTotal;
			
				// si bitmap, on lisse par defaut l'image
				if (_loader.content is Bitmap)
				{
					//var myBitmap:Bitmap = new Bitmap((_loader.content as Bitmap).bitmapData, "auto", true);
					//itemCompleted(myBitmap);

					(_loader.content as Bitmap).smoothing = true;
					itemCompleted(_loader.content);
				}else
				{
					itemCompleted(_loader.content);
				}
				

		}
		private function onMovieError(event:IOErrorEvent):void
		{
			trace("XXXXX  onLoadError fichier : " + _currentItem.url);
			removeMovieListeners();
		}
		private function removeMovieListeners():void
		{
			_loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onMovieComplete);
			_loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, itemProgress);
			_loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onMovieError);

		}
		
		
		
		
		
		
		

		//-----------------------------------------------------------------------
		private function itemProgress(evt:ProgressEvent):void
		{
			_percentCurrent 			= evt.bytesLoaded / evt.bytesTotal;
			
			// limite
			if ( isNaN(_percentCurrent) ) _percentCurrent = 0;
			if ( _percentCurrent < 0) _percentCurrent = 0;
			if ( _percentCurrent > 1) _percentCurrent = 1;
			
			_currentItem.percent 		= _percentCurrent;
			
			if ( isNaN(evt.bytesLoaded) ) _currentItem.bytesLoaded 	= 0;
			else _currentItem.bytesLoaded 	= evt.bytesLoaded;
			
			if ( isNaN(evt.bytesTotal) ) _currentItem.bytesTotal 	= 0;
			else _currentItem.bytesTotal 	= evt.bytesTotal;
			
			
			_percent = ( _nbItemLoaded + _percentCurrent) / _nbItem;
			var pourct:Number = Math.round(_percentCurrent * 100);
			//trace("        percentCurrent " + _currentItem.url + " : " + pourct + " %");
			
			
			// déclenchement event
			var myevt:LoaderEvent = new LoaderEvent(LoaderEvent.PROGRESS);
			myevt.percent = _percent;
			this.dispatchEvent(myevt);

		}
		private function itemCompleted(content:Object):void
		{
			var pourct:Number = Math.round(_percent * 100);
			_currentItem.content = content;
			_currentItem.completed = true;
			
		
			_nbItemLoaded++;
			
			// on retire de la pile l'item qui est completé
			if (!_isGroupItem)
			{
				
				popItemFromTabItem();
			}
			else
			{
				popItemFromTabItemGroup();
				
				if ( _tabObjectGroup.length <= 0) {
					_tabObjectToLoad[0].completed = true;
					popItemFromTabItem();
					_isGroupItem = false;
				}
				
			}
				
			loadNext();

		}
		private function popItemFromTabItem():void
		{
			_tabObjectToLoad.shift();
			
			/*var tab:Array = new Array();
			for (var i:int = 1; i < _tabObjectToLoad.length; i++)
			{
				tab.push(_tabObjectToLoad[i]);
			}
			
			_tabObjectToLoad = tab;*/
			
			
		}
		private function popItemFromTabItemGroup():void
		{
			
			_tabObjectGroup.shift();
			
			/*var tab:Array = new Array();
			for (var i:int = 1; i < _tabObjectGroup.length; i++)
			{
				tab.push(_tabObjectGroup[i]);
			}
			
			_tabObjectGroup = tab;*/
		}
		private function addItemInPriority(item:Object):void
		{
			_tabObjectToLoad.unshift(item);
			
			/*var tab:Array = new Array();
			tab.push(item);
			
			for (var i:int = 0; i < _tabObjectToLoad.length; i++)
			{
				tab.push(_tabObjectToLoad[i]);
			}
			
			_tabObjectToLoad = tab;*/
		}
	}
	

}
